﻿/*
 * This file is part of quickReminder, a software for gamers
 *
 * Copyright (C) 2006-2009 Lucas Romero
 *
 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with this program; if not, see <http://www.gnu.org/licenses/>.
 */
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Text;
using System.Xml.Serialization;
using ICSharpCode.SharpZipLib.Zip;

namespace LucasCode.PublicTypes
{
    //?? muss das hier rein? Macht das wirklich Sinn?
    //public class FileStatusChangedEventArgs : EventArgs
    //{
    //    MatchFile m_file;
    //    public MatchFile File
    //    {
    //        get { return m_file; }
    //        set { m_file = value; }
    //    }

    //    public FileStatusChangedEventArgs(MatchFile file)
    //    {
    //        m_file = file;
    //    }
    //};

    [Serializable]
    public class MatchArchive : IStaticDataSource
    {
        public class ParseSettings
        {
            public ParseSettings(string Aequitasname, ushort DemoVariationPercent)
            {
                this.Aequitasname = Aequitasname;
                this.DemoVariationPercent = DemoVariationPercent;
            }
            public string Aequitasname;
            public ushort DemoVariationPercent;
        };

        #region Private Properties
        private string m_Comment = String.Empty;
        /* lists */
        private List<MatchContestant> m_Contestants = new List<MatchContestant>();
        private List<MatchRound> m_Rounds = new List<MatchRound>();
        private List<MatchFile> m_Files = new List<MatchFile>();
        private List<MatchPlayer> m_TeamlessPlayers = new List<MatchPlayer>();

        private DateTime m_DateCreated = DateTime.Now;

        private StringCollection m_UserwithAeq = new StringCollection();

        private string m_League;
        private string m_Matchlink;
        private ZipFile m_ZipFile;
        private string m_Login;

        private DateTime m_MatchDate = DateTime.MinValue;
        
        #endregion

        #region Constructors

        public MatchArchive()
        {
            Gamename = "hl";
            Modname = "cstrike";
        }

        //wofür ist dieser Constructor da?
        /*~MatchArchive()
        {
            Close();
        }*/

        public MatchArchive(string filename)
        {
            Gamename = "hl";
            Modname = "cstrike";
            Filename = filename;
        }

        #endregion

        #region Properties
        [XmlIgnore]
        public MatchPlayer[] Players
        {
            get
            {
                int PlayerCount = 0, i = 0;
                foreach (MatchContestant team in m_Contestants)
                    PlayerCount += team.PlayerCount;
                PlayerCount += m_TeamlessPlayers.Count;
                MatchPlayer[] players = new MatchPlayer[PlayerCount];
                foreach (MatchContestant team in m_Contestants)
                {
                    team.Players.CopyTo(players, i);
                    i += team.PlayerCount;
                }
                TeamlessPlayers.CopyTo(players, i);
                return players;
            }
        }

        [XmlIgnore]
        public int Progress { get; private set; }

        [XmlIgnore]
        public string Filename { get; private set; }

        [XmlIgnore]
        public string Ladder
        {
            get
            {
                //?? was für einen sinn macht es die Liga aus dem Matchlink auszulesen?
                return m_League;
                /*if (string.IsNullOrEmpty(m_Matchlink))
                    return String.Empty;
                Match m = Regex.Match(m_Matchlink, @"^(.*?)\d+/$");
                if (m.Success)
                    return m.Groups[1].Value;
                return string.Empty;*/
            }
            set { m_League = value; }
        }

        [XmlIgnore]
        public MatchPlayer Owner
        {
            get
            {
                foreach (MatchPlayer p in Players)
                    if (p.IsArchiveOwner)
                        return p;
                return null;
            }
            set
            {
                foreach (MatchPlayer p in Players)
                    p.IsArchiveOwner = false;
                if (value != null)
                    value.IsArchiveOwner = true;
            }
        }

        [XmlIgnore]
        public MatchContestant OwnTeam
        {
            get
            {
                foreach (MatchContestant c in m_Contestants)
                    if (c.IsOwnerTeam)
                        return c;
                return null;
            }
            set
            {
                foreach (MatchContestant c in m_Contestants)
                    c.IsOwnerTeam = false;
                if (value != null)
                    value.IsOwnerTeam = true;
            }
        }

        [XmlIgnore]
        public MatchContestant Enemy
        {
            get
            {
                MatchContestant enemy = null;
                foreach (MatchContestant c in m_Contestants)
                {
                    if (!c.IsOwnerTeam)
                    {
                        if (enemy != null)
                            return null;
                        else
                            enemy = c;
                    }

                }
                return enemy;
            }
        }

        [XmlIgnore]
        public MatchContestant Winner
        {
            get
            {
                int Score1 = 0, Score2 = 0;
                foreach (MatchRound r in m_Rounds)
                {
                    Score1 += r.Scores[0];
                    Score2 += r.Scores[1];
                }
                if (Score1 > Score2)
                    return Contestants[0];
                else if (Score1 < Score2)
                    return Contestants[1];
                else
                    return null;
            }
        }

        [XmlIgnore]
        public MatchStatus Status
        {
            get
            {
                if (OwnTeam != null)
                {
                    int ownScore = 0, enemyScore = 0;
                    foreach (MatchRound r in m_Rounds)
                    {
                        ownScore += r.Scores[m_Contestants.IndexOf(OwnTeam)];
                        enemyScore += r.Scores[m_Contestants.IndexOf(Enemy)];
                    }
                    if (ownScore > enemyScore)
                        return MatchStatus.Won;
                    else if (ownScore < enemyScore)
                        return MatchStatus.Lost;
                    else if (ownScore == 0 && enemyScore == 0)
                        return MatchStatus.Undefined;
                    else
                        return MatchStatus.Drawn;
                }
                else
                    return MatchStatus.Undefined;
            }
        }

        [XmlIgnore]
        public MatchFile[] Replays
        {
            get
            {
                var reps = new List<MatchFile>();
                foreach (MatchFile f in m_Files)
                    if (f.FileType == MatchFileType.Replay)
                        reps.Add(f);
                return reps.ToArray();
            }
        }

        [XmlIgnore]
        public MatchFile[] Screenshots
        {
            get
            {
                var screens = new List<MatchFile>();
                foreach (MatchFile f in m_Files)
                    if (f.FileType == MatchFileType.Screenshot)
                        screens.Add(f);
                return screens.ToArray();
            }
        }

        [XmlIgnore]
        public MatchFile[] Aequitasfiles
        {
            get
            {
                var aeqs = new List<MatchFile>();
                foreach (MatchFile f in m_Files)
                    if (f.FileType == MatchFileType.Aequitas)
                        aeqs.Add(f);
                return aeqs.ToArray();
            }
        }

        [XmlIgnore]
        public string LoginCookie
        {
            get { return m_Login; }
            set { m_Login = value; }
        }

        public string[] UserwithAeq
        {
            get
            {
                var ids = new string[m_UserwithAeq.Count];
                m_UserwithAeq.CopyTo(ids, 0);
                return ids;
            }
            set
            {
                m_UserwithAeq.Clear();
                m_UserwithAeq.AddRange(value);
            }
        }
        
        public MatchPlayer[] TeamlessPlayers
        {
            get { return m_TeamlessPlayers.ToArray(); }
            set
            {
                m_TeamlessPlayers.Clear();
                m_TeamlessPlayers.AddRange(value);
            }
        }

        public DateTime DateCreated
        {
            get { return m_DateCreated; }
            set { m_DateCreated = value; }
        }

        public MatchFile[] Files
        {
            get { return m_Files.ToArray(); }
            set
            {
                m_Files.Clear();
                foreach (MatchFile f in value)
                {
                    AddFile(f);
                }
            }
        }

        public int FileCount
        {
            get
            {
                return m_Files.Count;
            }
        }

        public string Matchlink
        {
            get { return m_Matchlink; }
            set
            {
                m_Matchlink = value;
                if (!uint.TryParse(MatchHelper.getIDFromLink(value), out m_ID))
                    m_ID = 0;
            }
        }

        public MatchContestant[] Contestants
        {
            get { return m_Contestants.ToArray(); }
            set
            {
                m_Contestants.Clear();
                m_Contestants.AddRange(value);
            }
        }

        public MatchRound[] Rounds
        {
            get { return m_Rounds.ToArray(); }
            set
            {
                m_Rounds.Clear();
                m_Rounds.AddRange(value);
            }
        }

        public string Gamename { get; set; }
        public int GameVersion { get; set; }
        private uint m_ID;
        public uint ID
        {
            get { return m_ID; }
            set { m_ID = value; }
        }

        public string Modname { get; set; }

        public string Comment
        {
            get { return m_Comment; }
            set { m_Comment = value; }
        }

        public DateTime MatchDate
        {
            get { return m_MatchDate; }
            set { m_MatchDate = value; }
        }
        #endregion

        #region Methods
        public void AddFile(MatchFile f)
        {
            if (!m_Files.Contains(f))
            {
                f.Parent = this;
                lock (m_Files)
                    m_Files.Add(f);
            }
        }

        public void AddTeamlessPlayer(MatchPlayer player)
        {
            m_TeamlessPlayers.Add(player);
        }

        public void RemoveTeamlessPlayer(MatchPlayer player)
        {
            m_TeamlessPlayers.Remove(player);
        }

        public void AddRound(MatchRound round)
        {
            if (!containsRound(round))
                m_Rounds.Add(round);
        }

        public void RemoveRound(MatchRound round)
        {
            if (containsRound(round))
            {
                System.Diagnostics.Trace.WriteLine("Deleted Matchround " + round.Map + " with result " + round.Scores[0] + "-" + round.Scores[1]);
                m_Rounds.Remove(round);
            }
        }

        public void AddContestant(MatchContestant team)
        {
            m_Contestants.Add(team);
        }

        public void AddUserwithAeq(string userID)
        {
            if (!m_UserwithAeq.Contains(userID))
                m_UserwithAeq.Add(userID);
        }

        public Stream GetSource()
        {
            var m = new MemoryStream();
            var xml = new XmlSerializer(GetType());
            xml.Serialize(m, this);
            m.Seek(0, SeekOrigin.Begin);
            return m;
        }

        public bool containsFile(MatchFile file)
        {
            return containsFile(file.Filename);
        }

        public bool containsFile(string file)
        {
            bool found = false;
            foreach (MatchFile f in m_Files)
            {
                if (!f.IsArchived && file == f.Filename)
                {
                    found = true;
                    break;
                }
            }
            return found;
        }

        public bool containsPlayer(string userID)
        {
            foreach (MatchPlayer p in Players)
            {
                if (p.UserID == userID)
                    return true;
            }

            return false;
        }

        public bool containsContestant(string teamID)
        {
            foreach (MatchContestant c in Contestants)
            {
                if (c.ID == teamID)
                    return true;
            }

            return false;
        }

        public MatchPlayer getPlayer(string name, string userID, string gameID, bool createIfNotFound)
        {
            MatchPlayer[] players = Players;
            foreach (MatchPlayer p in players)
            {
                if (p.Equals(name, userID, gameID))
                {
                    //TODO: is this good?
                    if (!string.IsNullOrEmpty(name) && p.CurrentNick != name)
                        p.CurrentNick = name;
                    if (!string.IsNullOrEmpty(userID) && p.UserID != userID)
                        p.UserID = userID;
                    if (!string.IsNullOrEmpty(gameID) && p.GameID != gameID)
                        p.GameID = gameID;
                    return p;
                }
            }
            if (createIfNotFound)
            {
                MatchPlayer p = new MatchPlayer();
                p.CurrentNick = name;
                p.UserID = userID;
                p.GameID = gameID;
                m_TeamlessPlayers.Add(p);
                return p;
            }
            return null;
        }

        public MatchPlayer getPlayer(string userID)
        {
            MatchPlayer[] players = Players;
            foreach (MatchPlayer p in players)
            {
                if (p.UserID == userID)
                    return p;
            }

            return null;
        }

        public MatchContestant getContestant(string teamID)
        {
            foreach (MatchContestant c in Contestants)
            {
                if (c.ID == teamID)
                    return c;
            }

            return null;
        }

        public string GetMaps()
        {
            var b = new StringBuilder();
            foreach (MatchRound r in m_Rounds)
            {
                b.Append(r.Map);
                b.Append(" & ");
            }
            //remove " & " at the end
            if (b.Length >= 3)
                b.Remove(b.Length - 3, 3);
            return b.ToString();
        }

        public void AutoRenameFiles(ConsoleEventItemCollection consoleevents, ParseSettings p)
        {
            MatchFile[] screenshots = Screenshots;
            MatchFile[] replays = Replays;
            MatchFile[] aeqfiles = Aequitasfiles;

            foreach (MatchFile f in aeqfiles)
                f.TargetName = p.Aequitasname;

            #region Rename status screenshots
            foreach (ConsoleEventItem ev in consoleevents)
            {
                if (ev.EventType != ConsoleEventItemType.StatusScreenshotTaken)
                    continue;
                MatchFile closestscrmatch = null;
                long TimeDif = Int64.MaxValue;
                foreach (MatchFile f in screenshots)
                {
                    //has the screenshot already been autorenamed?
                    if (f.WasAutoRenamed)
                        continue;
                    long NewTimeDif = f.LastChanged.Subtract(ev.EventTime).Ticks;
                    if (NewTimeDif < TimeDif && NewTimeDif > 0)
                    {
                        closestscrmatch = f;
                        TimeDif = NewTimeDif;
                    }
                }
                if (closestscrmatch != null && TimeDif <= new TimeSpan(0, 0, 0, 10).Ticks)
                {
                    closestscrmatch.TargetName = "Status";
                    closestscrmatch.WasAutoRenamed = true;

                    if (!string.IsNullOrEmpty(closestscrmatch.Filename) && !string.IsNullOrEmpty(closestscrmatch.TargetName))
                        System.Diagnostics.Trace.WriteLine("AutoRenameFiles() Set Targetname for status screen " + closestscrmatch.Filename + " to " + closestscrmatch.TargetName);
                }
            }
            #endregion

            System.Diagnostics.Trace.WriteLine("Status Screenshots renamed successfully");

            #region Rename replays and roundscreenshots
            int DemoNum = 0;
            int renamedscreencount = 0;

            long averagesize = 0;
            int n = 0;
            foreach (MatchFile f in replays)
            {
                averagesize += f.Size;
                n++;
            }
            if (n > 0)
                averagesize = averagesize / n;

            foreach (MatchFile f in replays)
            {
                if (f.Size > averagesize * (1 - (double)p.DemoVariationPercent / 100))
                {
                    f.TargetName = String.Format("Round {0}", ++DemoNum);
                    f.WasAutoRenamed = true;

                    if (!string.IsNullOrEmpty(f.Filename)&&!string.IsNullOrEmpty(f.TargetName))
                        System.Diagnostics.Trace.WriteLine("AutoRenameFiles() Set Targetname for replay " + f.Filename + " to " + f.TargetName);
                    
                    MatchFile closestmatch = null;
                    long TimeDif = Int64.MaxValue;
                    foreach (MatchFile f2 in screenshots)
                    {
                        //has the screenshot already been autorenamed?
                        if (f2.WasAutoRenamed)
                            continue;
                        // we don't care if it was written before or after the replay has been stopped
                        long NewTimeDif = Math.Abs(f.LastChanged.Subtract(f2.LastChanged).Ticks);

                        if (NewTimeDif < TimeDif)
                        {
                            closestmatch = f2;
                            TimeDif = NewTimeDif;
                        }
                    }
                    if (closestmatch != null && TimeDif < TimeSpan.TicksPerMinute)
                    {
                        closestmatch.TargetName = String.Format("Round {0}", DemoNum);

                        if (!string.IsNullOrEmpty(closestmatch.Filename) && !string.IsNullOrEmpty(closestmatch.TargetName))
                            System.Diagnostics.Trace.WriteLine("AutoRenameFiles() Set Targetname for screenshot " + closestmatch.Filename + " to " + closestmatch.TargetName);

                        closestmatch.WasAutoRenamed = true;
                        renamedscreencount++;
                    }
                }
            }
            #endregion

            System.Diagnostics.Trace.WriteLine("Demos and Roundscreenshots renamed successfully");
        }

        public void Initialize(MatchArchive source)
        {
            Comment = source.Comment;
            Contestants = source.Contestants;
            DateCreated = source.DateCreated;
            Files = source.Files;
            Matchlink = source.Matchlink;
            Rounds = source.Rounds;

            MatchDate = source.MatchDate;
            Gamename = source.Gamename;
            Modname = source.Modname;
            TeamlessPlayers = source.TeamlessPlayers;

            //reconfigureQrID();
        }

        public bool hasAequitasLog(MatchPlayer player)
        {
            if (m_UserwithAeq.Contains(player.UserID))
            {
                return true;
            }
            return false;
        }

        public string getReplacedString(string s)
        {
            s = s.Replace(@"%enemy%", getReplacement(Enemy != null ? Enemy.Name : "#unknown#"));
            s = s.Replace(@"%ownteam%", getReplacement(OwnTeam != null ? OwnTeam.Name : "#unknown#"));
            s = s.Replace(@"%league%", getReplacement(m_League));
            s = s.Replace(@"%matchdate%", getReplacement(MatchDate.ToString("dd.MM.yy")));
            s = s.Replace(@"%maps%", getReplacement(GetMaps()));

            return s;
        }

        public MatchPlayer getPlayerwithqRID(int id)
        {
            foreach (MatchPlayer player in Players)
            {
                if (player.QrID == id)
                    return player;
            }

            return null;
        }

        private static string getReplacement(string s)
        {
            if (string.IsNullOrEmpty(s))
                return "#unknown#";
            else
            {
                char[] invalid1 = Path.GetInvalidPathChars();
                char[] invalid2 = Path.GetInvalidFileNameChars();

                foreach (char c in invalid1)
                {
                    s = s.Replace(c, '_');
                }
                foreach (char c in invalid2)
                {
                    s = s.Replace(c, '_');
                }
                return s;
            }
        }

        private bool containsRound(MatchRound round)
        {
            foreach (MatchRound r in m_Rounds)
            {
                if (r.Map == round.Map && r.Scores[0] == round.Scores[0] && r.Scores[1] == round.Scores[1])
                    return true;
            }
            
            return false;
        }

        //brauch ich das wirklich?
        /*public void Close()
        {
            if (m_ZipFile != null)
            {
                m_ZipFile.Close();
            }
        }*/

        #endregion
    }
}
